home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995 February: Tool Chest / Dev.CD Feb 95 / Dev.CD Feb 95.toast / Sample Code / Pascal Sample 3.0B10 / Source / Sample.p < prev    next >
Encoding:
Text File  |  1993-10-13  |  43.0 KB  |  1,324 lines  |  [TEXT/MPS ]

  1. PROGRAM Sample;
  2.  
  3. (******************************************************************************
  4. *
  5. *    Apple Macintosh Developer Technical Support
  6. *
  7. *    Main program for the Sample application
  8. *
  9. *    Program:    Sample 3.0
  10. *    file:        Sample.p - Pascal implementation
  11. *
  12. *    by:        Matt Deatherage
  13. *
  14. *    Copyright © 1988-1993 Apple Computer, Inc.
  15. *    All rights reserved.
  16. *
  17. *******************************************************************************
  18. * Sample.p contains the main entry point and the PROGRAM statement, but
  19. * relies heavily on other units linked in to implement its functionality.
  20. *
  21. * For non-technical details and the story behind this sample, see the
  22. * "Read Me - Sample" file distributed with this program.
  23. *
  24. * Segmentation strategy:
  25. *
  26. * Sample consists of four segments.  "Main" contains most of the code used
  27. * to run the application, including any linked-in MPW or THINK libraries,
  28. * the main program, all routines in the TrafficLights unit and some
  29. * utility routines.  "Dialogs" contains the code to do the major dialogs
  30. * the program presents -- the "Modify Circle" and "Edit Preferences" dialogs.
  31. * "Print" contains the code to handle printing, and that segment unloads
  32. * "Dialogs" to make more memory available for printing.  "Initialize" contains
  33. * the code we use once, at application startup, and is then discarded.
  34. *
  35. * "Save and restore" strategy:
  36. *
  37. * Several system states or resources have only one current owner.  These
  38. * include things like the system resource file, the current GrafPort, the
  39. * position of an open file, etc.  Sometimes code carefully saves and restores
  40. * all resources it uses to prevent problems where a system state changes
  41. * when you don't expect it.
  42. *
  43. * Sample generally takes the strategy of using what it needs.  If it needs
  44. * to draw into a GrafPort, it sets the current port to that GrafPort.  This
  45. * makes the program less vulnerable to things like old desk accessories that
  46. * change the GrafPort behind the application's back, but more importantly, it
  47. * means any part of our program that draws or needs a specific GrafPort
  48. * doesn't have to worry about what it already was.  We set it to what we
  49. * need each time.
  50. *
  51. * There are exceptions to this.  We have some routines which are called by
  52. * the system, or by other routines that shouldn't necessarily have to reset
  53. * the current GrafPort after each subroutine call.  In those cases, parameters
  54. * are saved and restored, and the routines are marked as such.
  55. *
  56. * Why the inconsistency?  Each way works better in certain cases.  If you
  57. * stick dogmatically to one philosophy or the other, you often wind up
  58. * inserting more code than you need just to be safe.  This is a pragmatic
  59. * approach that works pretty well for most situations.
  60. *
  61. * Memory strategy:
  62. *
  63. * Sample has windows, which have associated "document" structures hanging
  64. * off the window's refCon field.  We allocate the space for the window
  65. * record and for the document, and both are (non-relocatable) pointers
  66. * in our heap.  Window records have to be that way; document structures
  67. * are because we chose it for simplicity.
  68. *
  69. * If you do complex operations, you normally want to avoid pointers, using
  70. * relocatable handles instead.  However, since Sample is a fairly simple
  71. * application, we just make sure our heap is very unfragmented most of the
  72. * time.  We almost never lock handles for more than a few lines of code,
  73. * and all of our window records and document pointers are allocated low
  74. * in the heap.  Segments load high, and the rest is free space for handles
  75. * belonging to our window's GrafPorts, to print records or to other
  76. * system items like resources.  Most of our resources are purgeable as well.
  77. *
  78. ******************************************************************************)
  79.  
  80. (*******************************************************************************
  81. * Used Units
  82. *******************************************************************************)
  83.  
  84. USES Traps, Memory, Desk, DiskInit, OSEvents, Events, Fonts, ToolUtils, Scrap,
  85.      Errors, Balloons, Features, PrintTraps, AppleTalk, Processes, PPCToolbox,
  86.      EPPC, Notification, AppleEvents, SampleUtilities, TrafficLights,
  87.      SampleDialog;
  88.  
  89. (*******************************************************************************
  90. * Constants
  91. *******************************************************************************)
  92.  
  93. CONST
  94.  
  95. (*******************************************************************************
  96. * kNoEvents is the inverse of "every event" -- it means we don't want any
  97. * events.  We use this with WaitNextEvent to let the toolbox fill in the
  98. * mouse position and tick count without extracting any events from the
  99. * event queue.
  100. *******************************************************************************)
  101.  
  102.     kNoEvents = 0;                         { no events mask }
  103.  
  104. (*******************************************************************************
  105. * kMinHeap is the smallest heap we can possibly run in.  If this result:
  106. * ORD(GetApplLimit) - ORD(ApplicZone)
  107. * is less than kMinHeap, we don't have enough memory to run.  The
  108. * constant insures that we have enough memory for things that load
  109. * into our application heap under System 6 non-MultiFinder environments,
  110. * like reasonably-sized scraps, FKEYs and so forth.  In Sample's case,
  111. * the number is so small (under 100K) that there's no way it could
  112. * fail to run without MultiFinder except on a seriously wacked-out
  113. * system.
  114. * The number is largely determined by experience and looking at how much
  115. * memory you use in your heap, with debuggers and other heap examination
  116. * tools.  There's no magic formula you can use to figure this out for
  117. * you; you have to dive in and figure how much memory you really need and
  118. * then pad it a little bit.
  119. *******************************************************************************)
  120.  
  121.     kMinHeap = 60 * 1024;
  122.  
  123. (*******************************************************************************
  124. * kMinSpace is the minimum result from PurgeSpace, when called at initialization
  125. * time, for the application to run.  This number acts as a double-check to
  126. * insure that there really is enough memory for the application to run,
  127. * including what has been taken up already by pre-loaded resources, the scrap,
  128. * code and other sundry memory blocks.
  129. *******************************************************************************)
  130.  
  131.     kMinSpace = 24 * 1024;
  132.  
  133. (*******************************************************************************
  134. * kPrefSize is our preferred partition size, in KB.
  135. * kMinSize is our minimum parition size, in KB.
  136. *
  137. * You can find out more about these constants in Sample.h.
  138. *******************************************************************************)
  139.  
  140.     kPrefSize = 200;
  141.     kMinSize = 180;
  142.  
  143. (*******************************************************************************
  144. * kExtremeNeg and kExtremePos are used to set up wide-open rectangles and
  145. * regions.  kExtremePos has a -1 attached for historical reasons; it works
  146. * around a very old region bug.  It's not necessary anymore, but you might
  147. * see it, and that's why it's there.  It doesn't hurt.
  148. *******************************************************************************)
  149.  
  150.     kExtremeNeg = - 32768;
  151.     kExtremePos = 32767 - 1;         { required for old region bug }
  152.  
  153. (*******************************************************************************
  154. * Resource IDs, as found in Sample.r and Sample.h
  155. *******************************************************************************)
  156.  
  157.     rMenuBar = 128;                 { application's menu bar }
  158.     rAboutAlert = 128;                 { about Alert }
  159.  
  160.     rFeaturesNotPresent = 1000;        { Alert "some required features are missing" }
  161.     rNoMemoryForApp = 1001;            { Alert "Not enough memory to run" }
  162.  
  163. (*******************************************************************************
  164. * The following constants are used to identify menus and their items. The menu IDs
  165. * have an "m" prefix and the item numbers within each menu have an "i" prefix.}
  166. *******************************************************************************)
  167.  
  168.     mApple = 128;                     { Apple menu }
  169.     iAbout = 1;
  170.  
  171.     mFile = 129;                     { File menu }
  172.     iNew = 1;
  173.     iOpen = 2;
  174.     iClose = 4;
  175.     iSave = 5;
  176.     iSaveAs = 6;
  177.     iRevert = 7;
  178.     iPageSetup = 9;
  179.     iPrint = 10;
  180.     iQuit = 12;
  181.  
  182.     mEdit = 130;                     { Edit menu }
  183.     iUndo = 1;
  184.     iCut = 3;
  185.     iCopy = 4;
  186.     iPaste = 5;
  187.     iClear = 6;
  188.     iPrefs = 8;
  189.  
  190. (*******************************************************************************
  191. * These are for positioning the Disk Initialization dialogs.
  192. *******************************************************************************)
  193.  
  194.     kDITop = $0050;
  195.     kDILeft = $0070;
  196.  
  197. (*******************************************************************************
  198. * Global variables maintained by the program
  199. *******************************************************************************)
  200.  
  201. VAR
  202.  
  203. (*******************************************************************************
  204. * gInBackground is maintained by our osEvent handling routines.  Any part of
  205. * the program can check it to find out if we're currently in the background.
  206. *******************************************************************************)
  207.  
  208.     gQuitting: BOOLEAN;             { TRUE if we quit next time we go through
  209.                                       the main event loop }
  210.  
  211.     gInBackground: BOOLEAN;         { maintained by Initialize and DoEvent }
  212.  
  213.     cursorRgn: RgnHandle;             { cursorRgn contains a copy of the last 
  214.                                       mouseRgn we  passed to WaitNextEvent }
  215.  
  216. (*******************************************************************************
  217. * Forward declarations 
  218. *******************************************************************************)
  219.  
  220. FUNCTION Terminate: BOOLEAN;
  221.     FORWARD;
  222.  
  223. (******************************************************************************
  224. *
  225. * Public: _DataInit
  226. *
  227. * This routine is in the %A5Init segment of the MPW Pascal library.  We
  228. * reference it here so that we have an address inside that segment, and
  229. * we can unload it after we've launched to get it out of our heap.
  230. *
  231. * This does not compile if we're using THINK Pascal.
  232. *
  233. ******************************************************************************)
  234.  
  235. {$IFC UNDEFINED THINK_PASCAL}
  236.  
  237. PROCEDURE _DataInit;
  238.     EXTERNAL;
  239.  
  240. {$ENDC}
  241.  
  242. {$S Main}
  243. (******************************************************************************
  244. *
  245. * Public: AdjustMenus
  246. *
  247. * AdjustMenus enables and disables menu items based on the current state.
  248. * The user can only select enabled menu items.  We set up all the menu items
  249. * before we call MenuSelect or MenuKey.  We also set them up when the
  250. * current window changes (activate, deactivate, close and open).
  251. *
  252. * Note that while MenuSelect is the only time the user will see menu items,
  253. * the user can see menu _titles_ at any time.  This approach to deciding
  254. * what items to enable/disable for a menu has the advantage of concentrating
  255. * all decision-making in one routine.  However, it has the disadvantage of
  256. * requiring this routine to make decisions about whether documents are
  257. * dirty or not to enable the right file-handling items.  There are always
  258. * trade-offs.
  259. *
  260. ******************************************************************************)
  261.  
  262. PROCEDURE AdjustMenus;
  263.  
  264. VAR
  265.     window: WindowPtr;                { the frontmost window }
  266.     menu: MenuHandle;                { the menu we're adjusting }
  267.     theDoc: DocumentPtr;            { the frontmost window's document }
  268.     dirtyFlag: INTEGER;                { does the window need saving? }
  269.     menuDirty: BOOLEAN;                { do we need to redraw the menu bar? }
  270.  
  271. BEGIN
  272.     menuDirty := FALSE;                { assume we don't need to redraw }
  273.     
  274.     window := FrontWindow;
  275.     IF IsAppWindow(window) THEN
  276.         BEGIN
  277.             theDoc := DocumentPtr(GetWRefCon(FrontWindow));
  278.             dirtyFlag := GetDocumentDirtyFlag(theDoc);
  279.         END;
  280.  
  281.     { Do the File menu first.  Enable "close" for DAs but not much
  282.       else. }
  283.     
  284.     menu := GetMHandle(mFile);        { can't close a window that doesn't exist }
  285.     IF window = NIL THEN
  286.         DisableItem(menu, iClose)
  287.     ELSE
  288.         EnableItem(menu, iClose);
  289.  
  290.     IF (IsDAWindow(window) OR (window = NIL)) THEN
  291.         BEGIN                         { DAs can't use our menu items }
  292.             DisableItem(menu, iPageSetup);
  293.             DisableItem(menu, iPrint);
  294.             DisableItem(menu, iSave);
  295.             DisableItem(menu, iSaveAs);
  296.             DisableItem(menu, iRevert);
  297.         END
  298.     ELSE IF IsAppWindow(window) THEN
  299.         BEGIN
  300.             EnableItem(menu, iPageSetup);
  301.             EnableItem(menu, iPrint);
  302.             EnableItem(menu, iSaveAs);
  303.             
  304.             { allow saving if the document is new or dirty, but only
  305.               allow reverting if it's dirty, not if it's never been saved. }
  306.             
  307.             IF ((dirtyFlag = kDocumentDirty) AND (theDoc^.ourfileRefNum <> 0)) THEN
  308.                 EnableItem(menu, iRevert)
  309.             ELSE
  310.                 DisableItem(menu, iRevert);
  311.  
  312.             IF (dirtyFlag <> kDocumentClean) THEN
  313.                 EnableItem(menu, iSave)
  314.             ELSE
  315.                 DisableItem(menu, iSave);
  316.         END;
  317.  
  318.     { next, the Edit menu.  If a DA window is frontmost, we enable the
  319.       standard editing items, but the only one we support is Copy, which
  320.       copies an image of the document to the clipboard. }
  321.     
  322.     menu := GetMHandle(mEdit);
  323.     IF IsDAWindow(window) THEN
  324.         BEGIN                         { a desk accessory might need the edit menu }
  325.             EnableItem(menu, iUndo);
  326.             EnableItem(menu, iCut);
  327.             EnableItem(menu, iCopy);
  328.             EnableItem(menu, iPaste);
  329.             EnableItem(menu, iClear);
  330.         END
  331.     ELSE 
  332.         BEGIN                         { but we know we do not }
  333.             DisableItem(menu, iUndo);
  334.             DisableItem(menu, iCut);
  335.             IF window = NIL THEN
  336.                 DisableItem(menu, iCopy)
  337.             ELSE
  338.                 EnableItem(menu, iCopy);
  339.             DisableItem(menu, iClear);
  340.             DisableItem(menu, iPaste);
  341.         END;
  342.  
  343.     { Finally, the circle menu.  If there's no application window, disable
  344.       the whole menu.  Otherwise, enable items as they relate to the front
  345.       window. }
  346.     
  347.     menu := GetMHandle(mCircle);
  348.     IF IsAppWindow(window) THEN
  349.         BEGIN
  350.             menuDirty := NOT BTST(menu^^.enableFlags,0);
  351.             EnableItem(menu, 0); { This whole menu requires an app window
  352.                                    frontmost}
  353.             IF theDoc^.numCircles >= gPrefsRecord.maxNumCircles THEN
  354.                 DisableItem(menu, iAdd)
  355.             ELSE
  356.                 EnableItem(menu, iAdd);
  357.             IF theDoc^.numCircles = 1 THEN
  358.                 DisableItem(menu, iDelete)
  359.             ELSE
  360.                 EnableItem(menu, iDelete);
  361.         END
  362.     ELSE
  363.         BEGIN
  364.             menuDirty := BTST(menu^^.enableFlags,0);
  365.             DisableItem(menu, 0);
  366.         END;
  367.     IF menuDirty THEN
  368.         DrawMenuBar;
  369. END; { AdjustMenus }
  370.  
  371. {$S Main}
  372. (******************************************************************************
  373. *
  374. * Public: DoCloseWindow
  375. *
  376. * Closes a window.  Rather than deal with dirty flags and other document-
  377. * specific stuff here, we call CloseAppWindow, which returns TRUE if the
  378. * window actually closed, just as this routine does.
  379. *
  380. * "action" is either kClosing or kQuitting, so the right alert can be
  381. * presented to the user.
  382. *
  383. ******************************************************************************)
  384.  
  385. FUNCTION DoCloseWindow(window: WindowPtr; action: INTEGER): BOOLEAN;
  386.  
  387. BEGIN
  388.     DoCloseWindow := TRUE;
  389.     IF IsDAWindow(window) THEN
  390.         BEGIN
  391.             CloseDeskAcc(WindowPeek(window)^.windowKind);
  392.         END
  393.     ELSE IF IsAppWindow(window) THEN
  394.         BEGIN
  395.             DoCloseWindow := CloseAppWindow(window, action);
  396.         END;
  397.     AdjustMenus;
  398.     
  399. END; { DoCloseWindow }
  400.  
  401. {$S Main}
  402. (******************************************************************************
  403. *
  404. * Public: SampleQuitHandler
  405. *
  406. * This routine handles the 'quit' Apple Event, one of the four required ones.
  407. * If we got all the parameters from the Apple event, we call Terminate.
  408. * If it returns TRUE, we return noErr, otherwise the user cancelled.
  409. *
  410. ******************************************************************************)
  411.  
  412. FUNCTION SampleQuitHandler(quitAppleEvent: AppleEvent; reply: AppleEvent;
  413.                            handlerRefCon: LONGINT): OSErr;
  414.  
  415. VAR
  416.     myErr: OSErr;                    { errors from the system }
  417.  
  418. BEGIN
  419.     myErr := CheckRequiredAEParms(quitAppleEvent);
  420.     IF myErr = noErr THEN
  421.         BEGIN
  422.             IF Terminate THEN
  423.                 myErr := noErr
  424.             ELSE
  425.                 myErr := userCanceledErr;
  426.         END;
  427.     SampleQuitHandler := myErr;
  428. END; { SampleQuitHandler }
  429.  
  430. {$S Main}
  431. (******************************************************************************
  432. *
  433. * Public: SampleOpenAppHandler
  434. *
  435. * This routine handles the 'oapp' Apple event, one of the four required ones.
  436. * If we got all the parameters from the event, we know we're not going to
  437. * open any documents so we call DoNew to create a new, untitled window.
  438. *
  439. ******************************************************************************)
  440.  
  441. FUNCTION SampleOpenAppHandler(newAppleEvent: AppleEvent; reply: AppleEvent;
  442.                               handlerRefCon: LONGINT): OSErr;
  443.  
  444. VAR
  445.     myErr: OSErr;                    { errors from the system }
  446.     theWindow: WindowPtr;            { the window we create }
  447.  
  448. BEGIN
  449.     myErr := CheckRequiredAEParms(newAppleEvent);
  450.     IF myErr = noErr THEN
  451.         theWindow := DoNew;
  452.     SampleOpenAppHandler := myErr;
  453. END;
  454.  
  455. {$S Main}
  456. (******************************************************************************
  457. *
  458. * Public: SampleOpenDocHandler
  459. *
  460. * SampleOpenDocHandler handles the required 'odoc' event.  This event is
  461. * supposed to pass a list of alias records as the direct object (though
  462. * AppleScript apparently passes a pathname as a 'TEXT' descriptor), and we
  463. * retrieve them as FSSpec records, which are identical to our fileLikeSpec
  464. * records.  We then pass them to our DoOpenDocument routine to make windows
  465. * for them.
  466. *
  467. ******************************************************************************)
  468.  
  469. FUNCTION SampleOpenDocHandler(openAppleEvent: AppleEvent; reply: AppleEvent;
  470.                               handlerRefCon: LONGINT): OSErr;
  471.  
  472. VAR
  473.     myErr: OSErr;                        { errors from the system }
  474.     myDocList: AEDescList;                { the list of descriptors }
  475.     myFSSpec: FSSpec;                    { an FSSpec record for files }
  476.     numItems: LONGINT;                    { how many files are there? }
  477.     myKeyword: AEKeyword;                { ignored -- for AEGetNthPtr }
  478.     myType: DescType;                    { ignored -- real type of data returned }
  479.     realSize: Size;                        { ignored -- real size of data returned }
  480.     count: INTEGER;                        { loop variable counter }
  481.     theWindow: WindowPtr;                { the window we open }
  482.  
  483. BEGIN
  484.     myErr := AEGetParamDesc(openAppleEvent, keyDirectObject, typeAEList,
  485.                             myDocList);
  486.     IF myErr = noErr THEN
  487.         BEGIN
  488.             myErr := CheckRequiredAEParms(openAppleEvent);
  489.             IF myErr = noErr THEN
  490.                 BEGIN
  491.                     myErr := AECountItems(myDocList, numItems);
  492.                     IF myErr = noErr THEN
  493.                         BEGIN
  494.                             FOR count := 1 TO numItems DO
  495.                                 BEGIN
  496.                                     myErr := AEGetNthPtr(myDocList, count,
  497.                                                          typeFSS, myKeyword,
  498.                                                          myType, @myFSSpec,
  499.                                                          sizeof(FSSpec),
  500.                                                          realSize);
  501.                                     IF myErr = noErr THEN
  502.                                         theWindow := DoOpenDocument(
  503.                                                      fileLikeSpecPtr(@myFSSpec));
  504.  
  505.                                 END;
  506.                         END;
  507.                 END;
  508.         END;
  509.     SampleOpenDocHandler := myErr;
  510. END; { SampleOpenDocHandler }
  511.  
  512. {$S Main}
  513. (******************************************************************************
  514. *
  515. * Public: SamplePrintDocHandler
  516. *
  517. * This routine handles the required 'pdoc' event by printing documents
  518. * passed in the direct object of the Apple event WITHOUT opening windows
  519. * for them.
  520. *
  521. * We get a new print record, initialize it and if we can interact with the
  522. * user, ask him to name his job-specific parameters.  Then we process the
  523. * list of files in the Apple event like with SampleOpenDocHandler, but
  524. * this time we call DoPrintfile with the FSSpec and theMergeRec as a
  525. * PrJobMerge print record.
  526. ******************************************************************************)
  527.  
  528. FUNCTION SamplePrintDocHandler(printAppleEvent: AppleEvent; reply: AppleEvent;
  529.                                handlerRefCon: LONGINT): OSErr;
  530.  
  531. VAR
  532.     myErr: OSErr;
  533.     myDocList: AEDescList;
  534.     myFSSpec: FSSpec;
  535.     numItems: LONGINT;
  536.     myKeyword: AEKeyword;
  537.     myType: DescType;
  538.     realSize: Size;
  539.     count: INTEGER;
  540.     theDoc: DocumentPtr;
  541.     theMergeRec: THPrint;
  542.  
  543. BEGIN
  544.     myErr := noErr; theMergeRec := THPrint(NewHandle(sizeof(TPrint)));
  545.     IF theMergeRec <> NIL THEN
  546.     BEGIN
  547.         PrOpen;
  548.         PrintDefault(theMergeRec);
  549.         IF OKToInteract THEN
  550.             IF NOT PrJobDialog(theMergeRec) THEN
  551.                 myErr := userCanceledErr;
  552.         PrClose;
  553.         IF myErr = noErr THEN
  554.         BEGIN
  555.             myErr := AEGetParamDesc(printAppleEvent, keyDirectObject,
  556.                                     typeAEList, myDocList);
  557.             IF myErr = noErr THEN
  558.             BEGIN
  559.                 myErr := CheckRequiredAEParms(printAppleEvent);
  560.                 IF myErr = noErr THEN
  561.                 BEGIN
  562.                     myErr := AECountItems(myDocList, numItems);
  563.                     IF myErr = noErr THEN
  564.                     BEGIN
  565.                         FOR count := 1 TO numItems DO
  566.  
  567.                         BEGIN
  568.                             myErr := AEGetNthPtr(myDocList, count,
  569.                                      typeFSS, myKeyword, myType,
  570.                                        @myFSSpec, sizeof(FSSpec),
  571.                                        realSize);
  572.  
  573.                             IF myErr = noErr THEN
  574.                             BEGIN
  575.                                 DoPrintfile(fileLikeSpecPtr(@myFSSpec),
  576.                                             theMergeRec);
  577.                             END;
  578.  
  579.                         END;
  580.                     END;
  581.                 END;
  582.             END;
  583.         END;
  584.     END;
  585.     DisposeHandle(Handle(theMergeRec));
  586.     SamplePrintDocHandler := myErr;
  587. END; { SamplePrintDocHandler }
  588.  
  589. {$S Main}
  590. (******************************************************************************
  591. *
  592. * Public: RemoveAEHandlers
  593. *
  594. * This routine removes the four required event handlers, and then calls
  595. * RemoveAppAEHandlers to give other units a chance to remove any event handlers
  596. * they may have installed.
  597. *
  598. ******************************************************************************)
  599.  
  600. PROCEDURE RemoveAEHandlers;
  601.  
  602. VAR
  603.     myErr: OSErr;                    { error from the Apple Event manager }
  604.  
  605. BEGIN
  606.     myErr := AERemoveEventHandler(kCoreEventClass, kAEOpenApplication,
  607.                                   @SampleOpenAppHandler, FALSE);
  608.     myErr := AERemoveEventHandler(kCoreEventClass, kAEOpenDocuments,
  609.                                   @SampleOpenDocHandler, FALSE);
  610.     myErr := AERemoveEventHandler(kCoreEventClass, kAEPrintDocuments,
  611.                                   @SamplePrintDocHandler, FALSE);
  612.     myErr := AERemoveEventHandler(kCoreEventClass, kAEQuitApplication,
  613.                                   @SampleQuitHandler, FALSE);
  614.     RemoveAppAEHandlers; { give the application a chance to remove more handlers
  615.                            without modifying Sample.p }
  616. END; { RemoveAEHandlers }
  617.  
  618. {$S Main}
  619. (******************************************************************************
  620. *
  621. * Public: Terminate
  622. *
  623. * Terminate cleans up the application and exits.  We loop through all open
  624. * windows, asking DoCloseWindow to close each one.  If it ever returns
  625. * FALSE, we know a window couldn't be closed, so we abort the procedure.
  626. *
  627. * If all windows get closed, we call TerminateApplication to give other
  628. * units a chance to do cleanup as well.  Then we remove our Apple Event
  629. * handlers by calling RemoveAEHandlers.
  630. *
  631. * The routine returns TRUE if we're ready to quit.
  632. *
  633. ******************************************************************************)
  634.  
  635. FUNCTION Terminate: BOOLEAN;
  636.  
  637. VAR
  638.     aWindow: WindowPtr;                { the window to close }
  639.     closed,
  640.     okToQuit: BOOLEAN;
  641.  
  642. BEGIN
  643.     closed := TRUE;
  644.     REPEAT
  645.         aWindow := FrontWindow;     { get the current front window}
  646.         IF aWindow <> NIL THEN
  647.             closed := DoCloseWindow(aWindow, kQuitting);
  648.                                     { close this window }
  649.     UNTIL (NOT closed) | (aWindow = NIL); { do all windows }
  650.  
  651.     IF closed THEN
  652.         BEGIN
  653.             okToQuit := TerminateApplication;
  654.             IF gHasAppleEvents THEN
  655.                 RemoveAEHandlers;
  656.             gQuitting := (closed AND okToQuit);
  657.             Terminate := gQuitting;
  658.         END;
  659. END; { Terminate }
  660.  
  661. {$S Initialize}
  662. (******************************************************************************
  663. *
  664. * Public: InstallAEHandlers
  665. *
  666. * This routine installs the four required event handlers, and then calls
  667. * InstallAppAEHandlers to give other units a chance to install any event handlers
  668. * they may need.
  669. *
  670. ******************************************************************************)
  671.  
  672. PROCEDURE InstallAEHandlers;
  673.  
  674. VAR
  675.     myErr: OSErr;                    { error from the Apple Event manager }
  676.  
  677. BEGIN
  678.     myErr := AEInstallEventHandler(kCoreEventClass, kAEOpenApplication,
  679.                                    @SampleOpenAppHandler, 0, FALSE);
  680.     myErr := AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments,
  681.                                    @SampleOpenDocHandler, 0, FALSE);
  682.     myErr := AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments,
  683.                                    @SamplePrintDocHandler, 0, FALSE);
  684.     myErr := AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
  685.                                    @SampleQuitHandler, 0, FALSE);
  686.     InstallAppAEHandlers; { give the application a chance to install more
  687.                             handlers without modifying Sample.p }
  688. END;
  689.  
  690. {$S Initialize}
  691. (******************************************************************************
  692. *
  693. * Public: Initalize
  694. *
  695. * Sets up the whole world.  Initializes all the toolbox managers we use, 
  696. * determines if the features we require to operate are present, checks to make
  697. * sure our heap is large enough to run, installs our menus, installs our
  698. * Apple Events handlers, and asks other units if they have anything to
  699. * initialize.  If InitializeApplication returns FALSE, we terminate.
  700. *
  701. ******************************************************************************)
  702.  
  703. PROCEDURE Initialize;
  704.  
  705. VAR
  706.     menuBar: Handle;                    { our menu bar 'MBAR' resource }
  707.     total,                                { for PurgeSpace - all bytes available }
  708.     contig: LONGINT;                    { for PurgeSpace - contiguous memory }
  709.     ignoreResult: BOOLEAN;                { for EventAvail below }
  710.     event: EventRecord;                    { for EventAvail below }
  711.     count: INTEGER;                        { loop counter variable }
  712.  
  713. BEGIN
  714.     gInBackground := FALSE;
  715.     gQuitting := FALSE;
  716.  
  717.     { Allocate more master pointers.  We empirically determine that if we
  718.       opened the maximum number of windows our default partition allows, we
  719.       need about 10 more master pointer blocks, so we create them here. }
  720.     
  721.     FOR count := 1 TO 10 DO
  722.         MoreMasters;
  723.  
  724.     { Initialize the toolbox managers }
  725.     
  726.     InitGraf(@thePort);
  727.     InitFonts;
  728.     InitWindows;
  729.     InitMenus;
  730.     TEInit;
  731.     InitDialogs(NIL);
  732.     InitCursor;
  733.  
  734.     { You would initialize AppleTalk if you were using it at this Point.
  735.     
  736.       This magic incantation tells old MultiFinder that you're really alive
  737.       and ready to go -- it waits for three event calls to make sure
  738.       that you were serious about being an application and won't bring you
  739.       to the front until it sees the third one. }
  740.  
  741.     FOR count := 1 TO 3 DO
  742.         ignoreResult := EventAvail(everyEvent, event);
  743.  
  744.     { We call DetermineFeatures to see if we have the features we need. }
  745.  
  746.     IF NOT DetermineFeatures THEN
  747.         AlertUser(rFeaturesNotPresent);
  748.  
  749.     { Check to make sure the heap we have meets our minimum heap requirements,
  750.       in case the user cranked them down with ResEdit or something.  If we
  751.       don't have our minimum heap, we won't run. }
  752.  
  753.     IF ORD(GetApplLimit) - ORD(ApplicZone) < kMinHeap THEN
  754.         AlertUser(rNoMemoryForApp);
  755.  
  756.     { Next, make sure that enough memory is free for your application to run. The 
  757.       heap may have been of required size, but a large scrap was loaded which left
  758.       too little memory. To check for this, call PurgeSpace and compare the result 
  759.       with a value that you have determined is the minimum amount of free memory 
  760.       your application needs at initialization.
  761.       
  762.       This number can be derived several different ways. One way that is fairly
  763.       straightforward is to run the application in the minimum size configuration
  764.       as described previously. Call PurgeSpace at initialization and examine the value
  765.       returned. However, you should make sure that this result is not being modified
  766.       by the scrap's presence. You can do that by calling ZeroScrap before calling
  767.       PurgeSpace. Make sure to remove that call before shipping, though. }
  768.  
  769.     PurgeSpace(total, contig);
  770.     IF total < kMinSpace THEN
  771.         AlertUser(rNoMemoryForApp);
  772.  
  773.     { Now get our menus installed and ready to go. }
  774.  
  775.     menuBar := GetNewMBar(rMenuBar);     { read menus into menu bar }
  776.     SetMenuBar(menuBar);                 { install menus }
  777.     DisposeHandle(menuBar);
  778.     AddResMenu(GetMHandle(mApple), 'DRVR');
  779.                                         { add DA names to Apple menu}
  780.     { install the Apple Event handlers }
  781.  
  782.     IF gHasAppleEvents THEN
  783.         InstallAEHandlers;
  784.  
  785.     { Give another unit a chance to initialize itself }
  786.  
  787.     IF NOT InitializeApplication THEN
  788.         ignoreResult := Terminate;
  789.  
  790.     AdjustMenus;                        { fix our menu items }
  791.     DrawMenuBar;                        { force a draw the first time }
  792.  
  793. END; { Initialize }
  794.  
  795. {$S Main}
  796. (******************************************************************************
  797. *
  798. * Public: DoMenuCommand
  799. *
  800. * We call DoMenuCommand whenever the user chooses a menu item, with the mouse
  801. * or with a key equivalent.  It performs the right operation for each command.
  802. * It keeps things organized when you have both MenuSelect and MenuKey dispatch
  803. * to one central command-handling routine like this.
  804. *
  805. * This routine actually handles the Edit menu commands, because they're so
  806. * brief.
  807. *
  808. ******************************************************************************)
  809.  
  810.  
  811. PROCEDURE DoMenuCommand(menuResult: LONGINT);
  812.  
  813. VAR
  814.     menuID: INTEGER;                     { the ID of the selected menu }
  815.     menuItem: INTEGER;                     { the item number of the selected menu }
  816.     daName: Str255;                        { name of the DA to open }
  817.     daRefNum: INTEGER;                    { refNum of opened DA }
  818.     handledByDA: BOOLEAN;                { TRUE if an event was handled by a DA }
  819.     ignore: BOOLEAN;                    { ignored result of function }
  820.     theDoc: DocumentPtr;                 { document for a window }
  821.     theWindow: WindowPtr;                 { for making a new window }
  822.     ourPicture: PicHandle;                { Picture to put in the scrap }
  823.     lErr: LONGINT;                        { error from Scrap Manager }
  824.     myHandle : Handle;                    { memory cushion Handle }
  825.  
  826. BEGIN
  827.     
  828.     { Using HiWrd and LoWrd instead of HiWord and LoWord avoids going through
  829.       the trap dispatcher in all cases, instead of relying on your development
  830.       environment to do it }
  831.     
  832.     menuID := HiWrd(menuResult);
  833.     menuItem := LoWrd(menuResult);
  834.  
  835.     CASE menuID OF
  836.         mApple:
  837.             CASE menuItem OF
  838.                 iAbout:                 { bring up Alert for About }
  839.                     AlertUser(rAboutAlert);
  840.                 OTHERWISE
  841.                     BEGIN                 { all non-About items in this menu are DAs }
  842.                         GetItem(GetMHandle(mApple), menuItem, daName);
  843.                         daRefNum := OpenDeskAcc(daName);
  844.                     END;
  845.             END;
  846.  
  847.         mFile:
  848.             CASE menuItem OF
  849.                 iNew:
  850.                     theWindow := DoNew;
  851.                 iOpen:
  852.                     theWindow := DoOpenDocument(NIL);
  853.                 iClose:
  854.                     ignore := DoCloseWindow(FrontWindow, kClosing);
  855.                     { we don't care if the user cancels this operation }
  856.                 iQuit:
  857.                     ignore := Terminate;
  858.                 iPageSetup:
  859.                     ignore := DoPageSetup(FrontWindow);
  860.                 iPrint:
  861.                     ignore := DoPrint(FrontWindow);
  862.                 iSave:
  863.                     ignore := DoSave(FrontWindow);
  864.                 iSaveAs:
  865.                     ignore := DoSaveAs(FrontWindow);
  866.                 iRevert:
  867.                     ignore := DoRevert(FrontWindow);
  868.             END;
  869.  
  870.         mEdit:                             { call SystemEdit for DA editing & 
  871.                                           MultiFinder/Process Manager }
  872.             CASE menuItem OF
  873.                 iPrefs:
  874.                     BEGIN
  875.                         
  876.                         { check to make sure there's enough memory to do
  877.                           the preferences dialog first! }
  878.                         
  879.                         myHandle := NewHandle(kDialogMemorySize);
  880.                         DisposeHandle(myHandle);
  881.                         IF myHandle = NIL THEN
  882.                             AlertUser(rNoMemoryForOperation)
  883.                         ELSE
  884.                             DoEditPreferences;
  885.                     END;
  886.                 iCopy:
  887.                     BEGIN
  888.                         IF IsDAWindow(FrontWindow) THEN
  889.                             handledByDA := SystemEdit(menuItem - 1)
  890.                         ELSE
  891.                             BEGIN
  892.                                 
  893.                                 { Get a PICT of our document and make it one of
  894.                                   the scraps [is a boy dog] }
  895.                                 
  896.                                 ourPicture := MakeDocumentPicture(DocumentPtr(
  897.                                               GetWRefCon(FrontWindow)));
  898.                                 lErr := ZeroScrap;
  899.                                 HLock(Handle(ourPicture));
  900.                                 lErr := PutScrap(GetHandleSize(Handle(ourPicture)),
  901.                                                 'PICT',
  902.                                                  StripAddress(Handle(ourPicture)^));
  903.                             END;
  904.                     END;
  905.                 OTHERWISE
  906.                     handledByDA := SystemEdit(menuItem - 1); 
  907.                     { since we don't do any editing }
  908.             END;
  909.  
  910.         mCircle:
  911.             CASE menuItem OF
  912.                 iAdd:
  913.                     AddDefaultCircle(DocumentPtr(GetWRefCon(FrontWindow)),
  914.                                      FrontWindow);
  915.                 iDelete:
  916.                     DeleteActiveCircle(FrontWindow);
  917.                 iModify:
  918.                     BEGIN
  919.                         theDoc := DocumentPtr(GetWRefCon(FrontWindow));
  920.                         SetPort(FrontWindow);
  921.                         IF ChangeCircleOptions(theDoc^.circleArray
  922.                                                [theDoc^.activeCircle]) THEN
  923.                                 SetDocumentDirtyFlag(theDoc, kDocumentDirty);
  924.                     END;
  925.             END;
  926.  
  927.         OTHERWISE; { The System takes care of Apple, Help and Application menus
  928.                       for us when we call MenuSelect, but it's good form to have all 
  929.                      cases covered with a statement. }
  930.     END;
  931.     HiliteMenu(0); { unhighlight what MenuSelect (or MenuKey) hilited }
  932. END; { DoMenuCommand }
  933.  
  934. {$Z+} { makes this routine available for SampleUtilities.p }
  935. {$S Main}
  936. (******************************************************************************
  937. *
  938. * Public: DoUpdate
  939. *
  940. * This is called when we receive an update event for a window.  It calls
  941. * DrawWindow to draw the contents of an application window.  We only call
  942. * the routine if the visRgn is non-empty.  Since BeginUpdate swaps the
  943. * updateRgn and the visRgn, the visRgn will be empty if there is no drawing
  944. * to do.
  945. *
  946. ******************************************************************************)
  947.  
  948.  
  949. PROCEDURE DoUpdate(window: WindowPtr);
  950.  
  951. BEGIN
  952.     IF IsAppWindow(window) THEN
  953.         BEGIN
  954.             BeginUpdate(window);         { sets up the visRgn, clears updateRgn }
  955.             IF NOT EmptyRgn(window^.visRgn) THEN
  956.                 { we pass FALSE because updating is not printing }
  957.                 DrawWindow(window, NIL, FALSE, window = FrontWindow);
  958.             EndUpdate(window);             { restores the visRgn }
  959.         END;
  960. END; { DoUpdate }
  961.  
  962. {$Z+} { makes this routine available for SampleUtilities.p }
  963. {$S Main}
  964. (******************************************************************************
  965. *
  966. * Public: DoActivate
  967. *
  968. * This is called when a window is activated or deactivated.  in Sample, the
  969. * window Manager's handling of these events suits us just fine.  Other
  970. * applications might have TextEdit records, controls, lists or other visual
  971. * things to redraw.  We Handle this by invalidating the window and letting
  972. * our drawing routines redraw the window in an inactive state.
  973. *
  974. * We do _not_ invalidate if it's a context switch (a suspend or resume event).
  975. * Our front window looks the same when we're in the background, so invalidating
  976. * on suspend/resume events would cause an unnecessary blink. We similarly only
  977. * redraw the menu states if we're changing an application window.
  978. ******************************************************************************)
  979.  
  980. PROCEDURE DoActivate(window: WindowPtr; becomingActive, contextSwitch: BOOLEAN);
  981.  
  982. BEGIN
  983.     IF IsAppWindow(window) THEN
  984.         BEGIN
  985.             SetPort(window);
  986.             IF NOT contextSwitch THEN
  987.                 InvalRect(window^.portRect);
  988.             AdjustMenus;
  989.         END;
  990. END; { DoActivate }
  991.  
  992. {$S Main}
  993. (******************************************************************************
  994. *
  995. * Public: GetGlobalMouse
  996. *
  997. * This routine uses OSEventAvail with the kNoEvents constant to fill in the
  998. * event record with the current global mouse position and modifiers without
  999. * retrieving any events from the queue.  You could do this with GetMouse
  1000. * and LocalToGlobal, but that requires there to be a currently valid GrafPort
  1001. * set (not a bad idea, but not always TRUE during initialization).
  1002. *
  1003. * We're not interested in the event, just the mouse position.
  1004. *
  1005. ******************************************************************************)
  1006.  
  1007. PROCEDURE GetGlobalMouse(VAR mouse: Point);
  1008.  
  1009. VAR
  1010.     event: EventRecord;                    { the event record we use }
  1011.  
  1012. BEGIN
  1013.     IF OSEventAvail(kNoEvents, event) THEN
  1014.         ;
  1015.     mouse := event.where; 
  1016. END;
  1017.  
  1018. {$S Main}
  1019. (******************************************************************************
  1020. *
  1021. * Public: AdjustCursor
  1022. *
  1023. * Changes the Cursor, depending on its position.  This also calculates the
  1024. * Region where the current Cursor resides, for WaitNextEvent.  We get a
  1025. * "mouse moved" event when the Cursor is moved outside that Region, so then
  1026. * we're called again and we recalculate the Region.
  1027. *
  1028. * This routine was called more frequently, and was more complicated, in
  1029. * previous versions of Sample.  It used to create a rectangular region based
  1030. * on a window's portRect and intersect it with the visRgn.  Since the visRgn
  1031. * is in local coordinates but the contentRgn is in global coordinates, this
  1032. * involved a fair amount of calculation, all of which was unnecessary because
  1033. * a) we only do this for our frontmost window, which is always completely
  1034. *    visible (or both the contentRgn and visRgn are clipped off a monitor
  1035. *     equally), and
  1036. * b) we can't move the mouse into any part of the content region that isn't
  1037. *     on-screen.
  1038. *
  1039. * So now we use the contentRgn of the front window for the region to use
  1040. * the plus cursor in.
  1041. *
  1042. * If balloon help is on, and we're over the window's content region, we
  1043. * call DoHelp to give other units a chance to display dynamic balloon help
  1044. * while we're already calculating regions and mouse positions and such.
  1045. * DoHelp changes the region we pass it to reflect when it would like
  1046. * another chance to display a balloon, or when it would like the Help Manager
  1047. * to remove the current balloon.
  1048. *
  1049. ******************************************************************************)
  1050.  
  1051. PROCEDURE AdjustCursor(mouse: Point);
  1052.  
  1053. VAR
  1054.     window: WindowPtr;                    { the front window }
  1055.     arrowRgn: RgnHandle;                { region to use arrow cursor in }
  1056.     plusRgn: RgnHandle;                    { region to use plus cursor in }
  1057.     appWindow: BOOLEAN;                    { TRUE if this window is ours }
  1058.  
  1059. BEGIN
  1060.     window := FrontWindow;
  1061.     appWindow := IsAppWindow(window);
  1062.     IF NOT gInBackground THEN             { we only adjust the cursor when we are
  1063.                                           in front }
  1064.         BEGIN
  1065.         
  1066.             { calculate regions for different cursor shapes }
  1067.             
  1068.             arrowRgn := NewRgn;
  1069.             plusRgn := NewRgn;
  1070.  
  1071.             { start with a big, big rectangular region }
  1072.             
  1073.             SetRectRgn(arrowRgn, kExtremeNeg, kExtremeNeg, kExtremePos,
  1074.                        kExtremePos);
  1075.             IF window <> NIL THEN
  1076.                 SetPort(window);         { for LocalToGlobal purposes inside DoHelp }
  1077.  
  1078.             { calculate plusRgn }
  1079.  
  1080.             IF appWindow THEN
  1081.                 CopyRgn(WindowPeek(window)^.contRgn, plusRgn)
  1082.             ELSE
  1083.                 SetEmptyRgn(plusRgn);
  1084.  
  1085.             { subtract plus region from arrowRgn }
  1086.             
  1087.             DiffRgn(arrowRgn, plusRgn, arrowRgn);
  1088.  
  1089.             { change the Cursor and the Region }
  1090.             IF PtInRgn(mouse, plusRgn) THEN
  1091.                 BEGIN
  1092.                     SetCursor(GetCursor(plusCursor)^^);
  1093.                     CopyRgn(plusRgn, cursorRgn);
  1094.                     
  1095.                     { Give the window-supporting code a chance to do balloon help 
  1096.                       if we're over the window's content region. }
  1097.                       
  1098.                     IF gHasHelpMgr AND appWindow THEN
  1099.                         IF HMGetBalloons THEN
  1100.                             BEGIN
  1101.                                 DoHelp(mouse, cursorRgn);
  1102.                             END
  1103.                 END
  1104.             ELSE
  1105.                 BEGIN
  1106.                     SetCursor(arrow);
  1107.                     CopyRgn(arrowRgn, cursorRgn);
  1108.                 END;
  1109.  
  1110.             { get rid of our local regions }
  1111.             
  1112.             DisposeRgn(arrowRgn);
  1113.             DisposeRgn(plusRgn);
  1114.  
  1115.         END;
  1116. END; { AdjustCursor }
  1117.  
  1118. {$Z+} { makes this routine available for SampleUtilities.p }
  1119. {$S Main}
  1120. (******************************************************************************
  1121. *
  1122. * Public: DoEvent
  1123. *
  1124. * This routine does the right thing for all the events we can get.
  1125. *
  1126. ******************************************************************************)
  1127.  
  1128. PROCEDURE DoEvent(event: EventRecord);
  1129.  
  1130. VAR
  1131.     part,                                { window part from FindWindow }
  1132.     err: INTEGER;                        { result from DIBadMount }
  1133.     window: WindowPtr;                    { window we found an event in }
  1134.     ignore: BOOLEAN;                    { result from DoCloseWindow }
  1135.     key: CHAR;                            { key for keyDown/autoKey events }
  1136.     aPoint: Point;                        { Point for DIBadMount dialog }
  1137.     myErr: OSErr;                        { error from Apple Event Manager }
  1138.  
  1139. BEGIN
  1140.     CASE event.what OF
  1141.         mouseDown:
  1142.             BEGIN
  1143.                 part := FindWindow(event.where, window);
  1144.                 CASE part OF
  1145.                     inDesk: ;
  1146.                     inMenuBar:
  1147.                         BEGIN             { process the menu command     }
  1148.                             AdjustMenus;
  1149.                             SetCursor(arrow);
  1150.                             DoMenuCommand(MenuSelect(event.where));
  1151.                             AdjustCursor(event.where);
  1152.                         END;
  1153.                     inSysWindow:         { let the system handle the mouseDown }
  1154.                         SystemClick(event, window);
  1155.                     inContent:
  1156.                         BEGIN
  1157.                             IF window <> FrontWindow THEN
  1158.                                 BEGIN
  1159.                                     SelectWindow(window);
  1160.                                     
  1161.                                     { If you want to process the click in the
  1162.                                       window as well as select it, call DoEvent
  1163.                                       again here.  If you want that to work
  1164.                                       not only for your windows but when you're
  1165.                                       switched in by the Process Manager or
  1166.                                       MultiFinder, add the "GetFrontClicks" bit
  1167.                                       to your 'SIZE' resource flags }
  1168.  
  1169.                                 END
  1170.                             ELSE
  1171.                                 DoContentClick(window, event);
  1172.                             AdjustCursor(event.where);
  1173.                         END;
  1174.                     inDrag:
  1175.                         BEGIN
  1176.                             AdjustCursor(event.where);
  1177.                             { pass screenBits.bounds to get all gDevices }
  1178.                             DragWindow(window, event.where, screenBits.bounds);
  1179.                         END;
  1180.                     inGrow: ;
  1181.                     inZoomIn, inZoomOut: ;
  1182.                     inGoAway:
  1183.                         IF TrackGoAway(window, event.where) THEN
  1184.                             ignore := DoCloseWindow(window, kClosing);
  1185.                             { we don't care if the user cancels this }
  1186.                 END;
  1187.             END;
  1188.         keyDown, autoKey:
  1189.             BEGIN                         { check for MenuKey equivalents }
  1190.                 key := CHR(BAND(event.message, charCodeMask));
  1191.                 IF BAND(event.modifiers, cmdKey) <> 0 THEN { Command key down }
  1192.                     IF event.what = keyDown THEN
  1193.                         BEGIN
  1194.                             AdjustMenus;
  1195.                             DoMenuCommand(MenuKey(key));
  1196.                             AdjustCursor(event.where);
  1197.                         END;
  1198.             END; 
  1199.         activateEvt: 
  1200.             BEGIN
  1201.                 
  1202.                 { call DoActivate with the window and TRUE for activate, FALSE for
  1203.                   deactivate }
  1204.                 
  1205.                 DoActivate(WindowPtr(event.message), 
  1206.                            BAND(event.modifiers,activeFlag) <> 0, FALSE);
  1207.             END;
  1208.         updateEvt:                         {call DoUpdate with the window to update}
  1209.             
  1210.             { call DoUpdate with the window to update }
  1211.             
  1212.             DoUpdate(WindowPtr(event.message));
  1213.         diskEvt:
  1214.             
  1215.             { Call DIBadMount in response to this so the user can format a floppy }
  1216.             
  1217.             IF HiWrd(event.message) <> noErr THEN
  1218.                 BEGIN
  1219.                     SetPt(aPoint, kDILeft, kDITop);
  1220.                     err := DIBadMount(aPoint, event.message);
  1221.                 END;
  1222.         osEvt:
  1223.             BEGIN
  1224.                 AdjustCursor(event.where);
  1225.                 CASE BAND(BRotL(event.message, 8), $FF) OF { high byte of message }
  1226.                     SuspendResumeMessage:
  1227.                         
  1228.                         { set gInBackground based on whether we're suspending or
  1229.                           resuming, then activate the front window accordingly }
  1230.                         
  1231.                         BEGIN
  1232.                             gInBackground := (BAND(event.message, resumeFlag) = 0);
  1233.                             DoActivate(FrontWindow, NOT gInBackground, TRUE);
  1234.                         END;
  1235.                     mouseMovedMessage:
  1236.                             { We'd call AdjustCursor here, except we do it on all
  1237.                               osEvt types so it's superfluous here }
  1238.                 END;
  1239.             END;
  1240.         kHighLevelEvent:
  1241.             BEGIN
  1242.                 IF gHasAppleEvents THEN
  1243.                     BEGIN
  1244.                         myErr := AEProcessAppleEvent(event);
  1245.                         AdjustCursor(event.where);
  1246.                     END;
  1247.             END;
  1248.         OTHERWISE;                        { prevents bad CASE label errors }
  1249.     END;
  1250. END; { DoEvent }
  1251.  
  1252. {$S Main}
  1253. (******************************************************************************
  1254. *
  1255. * Public: EventLoop
  1256. *
  1257. * Get events forever and Handle them with DoEvent.  Sample is only tested
  1258. * under System 6.0.7 and later, and WaitNextEvent is always implemented
  1259. * under System 6.0 and later, so we never call GetNextEvent.
  1260. *
  1261. ******************************************************************************)
  1262.  
  1263. PROCEDURE EventLoop;
  1264.  
  1265. VAR
  1266.     gotEvent: BOOLEAN;                    { TRUE if we got an event }
  1267.     event: EventRecord;                    { the event we're getting }
  1268.     mouse: Point;                        { the initial mouse position }
  1269.  
  1270. BEGIN
  1271.     cursorRgn := NewRgn;
  1272.     GetGlobalMouse(mouse);                 { find the current mouse position }
  1273.     AdjustCursor(mouse);                 { set up the cursor once }
  1274.     REPEAT
  1275.  
  1276.         { put us "asleep" forever under MultiFinder or System 7 }
  1277.         
  1278.         gotEvent := WaitNextEvent(everyEvent, event, MAXLONGINT, cursorRgn);
  1279.  
  1280.         IF gotEvent THEN
  1281.             DoEvent(event);
  1282.         
  1283.         { If you are using modeless dialogs that have editText items, you'll
  1284.           want to call IsDialogEvent to give the caret a chance to blink,
  1285.           even if WaitNextEvent returns FALSE.  However, be sure FrontWindow
  1286.           says there's a window before calling IsDialogEvent. }
  1287.         
  1288.     UNTIL gQuitting;                     { loop until our Terminate routine 
  1289.                                           says to stop}
  1290. END; { EventLoop }
  1291.  
  1292. {$S Main}
  1293. (******************************************************************************
  1294. *
  1295. * The main program entry point.  Expand our heap, initialize, unload the
  1296. * initialize segment and handle events forever.
  1297. *
  1298. ******************************************************************************)
  1299.  
  1300. BEGIN
  1301.  
  1302. {$IFC UNDEFINED Think_Pascal}
  1303.     UnloadSeg(@_DataInit);                 { note that _DataInit must not be in Main! }
  1304. {$ENDC}
  1305.  
  1306.     { If you have stack requirements that differ from the default,
  1307.       then you could use SetApplLimit to increase stack space at 
  1308.       this Point, before calling MaxApplZone. }
  1309.  
  1310.     MaxApplZone;                         { expand the heap so code segments load at
  1311.                                           the top}
  1312.  
  1313.     Initialize;                         { initialize the program }
  1314.     UnloadSeg(@Initialize);             { note that Initialize must not be in Main! }
  1315.  
  1316.     EventLoop;                             { call the main event loop }
  1317. END.
  1318.